/*
 * Copyright 1999-2101 Alibaba Group.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.fastjson.serializer;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONAware;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONStreamAware;

/**
 * 
 * @author wenshao<szujobs@hotmail.com>
 * 
 */
public class JSONSerializer {
	private final JSONSerializerMap mapping;

	private int features = JSON.DEFAULT_GENERATE_FEATURE;

	private final SerializeWriter out;

	private List<PropertyFilter> propertyFilters = null;

	public List<PropertyFilter> getPropertyFilters() {
		if (propertyFilters == null) {
			propertyFilters = new ArrayList<PropertyFilter>();
		}
		
		return propertyFilters;
	}
	
	public List<PropertyFilter> getPropertyFiltersDirect() {
		return propertyFilters;
	}

	public JSONSerializer() {
		this(new SerializeWriter(), JSONSerializerMap.getGlobalInstance());
	}

	public JSONSerializer(SerializeWriter out) {
		this(out, JSONSerializerMap.getGlobalInstance());
	}

	public JSONSerializer(JSONSerializerMap mapping) {
		this(new SerializeWriter(), mapping);
	}

	public JSONSerializer(SerializeWriter out, JSONSerializerMap mapping) {
		this.out = out;
		this.mapping = mapping;
	}

	public SerializeWriter getWrier() {
		return out;
	}

	public void close() {
		out.close();
	}

	public String toString() {
		return out.toString();
	}

	public void config(SerializerFeature feature, boolean state) {
		if (state) {
			features |= feature.getMask();
		} else {
			features &= ~feature.getMask();
		}
	}

	public boolean isEnabled(SerializerFeature feature) {
		return SerializerFeature.isEnabled(this.features, feature);
	}

	public JSONSerializerMap getMapping() {
		return mapping;
	}

	public static final void write(Writer out, Object object) {
		SerializeWriter writer = new SerializeWriter();
		try {
			JSONSerializer serializer = new JSONSerializer(writer);
			serializer.write(object);
			writer.writeTo(out);
		} catch (IOException ex) {
			throw new JSONException(ex.getMessage(), ex);
		} finally {
			writer.close();
		}
	}

	public static final void write(SerializeWriter out, Object object) {
		JSONSerializer serializer = new JSONSerializer(out);
		serializer.write(object);
	}

	public final void write(Object object) {
		try {
			if (object == null) {
				out.append("null");
				return;
			}

			Class<?> clazz = object.getClass();

			ObjectSerializer writer = getObjectWriter(clazz);

			writer.write(this, object);
		} catch (IOException e) {
			throw new JSONException(e.getMessage(), e);
		}
	}

	public ObjectSerializer getObjectWriter(Class<?> clazz) {
		ObjectSerializer writer = mapping.get(clazz);

		if (writer == null) {

			if (Map.class.isAssignableFrom(clazz)) {
				mapping.put(clazz, MapSerializer.instance);
			} else if (List.class.isAssignableFrom(clazz)) {
				mapping.put(clazz, ListSerializer.instance);
			} else if (Collection.class.isAssignableFrom(clazz)) {
				mapping.put(clazz, CollectionSerializer.instance);
			} else if (Date.class.isAssignableFrom(clazz)) {
				mapping.put(clazz, DateSerializer.instance);
			} else if (JSONAware.class.isAssignableFrom(clazz)) {
				mapping.put(clazz, JSONAwareSerializer.instance);
			} else if (JSONStreamAware.class.isAssignableFrom(clazz)) {
				mapping.put(clazz, JSONStreamAwareSerializer.instance);
			} else if (clazz.isEnum()) {
				mapping.put(clazz, EnumSerializer.instance);
			} else if (clazz.isArray()) {
				Class<?> componentType = clazz.getComponentType();
				ObjectSerializer compObjectSerializer = getObjectWriter(componentType);
				mapping.put(clazz, new ArraySerializer(compObjectSerializer));
			} else {
				mapping.put(clazz, new JavaBeanSerializer(clazz));
			}

			writer = mapping.get(clazz);
		}
		return writer;
	}
}
